﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Reflection;

using CANLib;

namespace CAN_Monitor
{
    public partial class frmMain : Form
    {
        #region ctor

        public frmMain()
        {
            InitializeComponent();

            LoadTableHeaders();

            this.mWatchFilter.DefaultRule = true;
        }

        #endregion

        #region Members

        private frmFilterBuilder mBuilder = null;
        private frmDiagram mDiagram = null;
        private frmValueWatch mWatch = null;
        private HardwareCommunicator mCommunicator = null;

        private PacketFilter mCurrentFilter = new PacketFilter("");
        private PacketFilter mWatchFilter = new PacketFilter("");

        private List<CANPacket> mPacketList = new List<CANPacket>();
        private List<CANPacket> mChangeList = new List<CANPacket>();
        private bool mPacketListChanged = false;
        private List<string> mErrors = new List<string>();

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the filter string in this form
        /// </summary>
        public String FilterString
        {
            get
            {
                return txtFilterString.Text;
            }
            set
            {
                txtFilterString.Text = value;
            }
        }

        /// <summary>
        /// Gets or sets the filter for the watch window
        /// </summary>
        public String WatchFilterString
        {
            get
            {
                return mWatchFilter.FilterString;
            }
            set
            {
                mWatchFilter.FilterString = value;
            }
        }

        /// <summary>
        /// Gets a list of all packets currently saved
        /// </summary>
        public CANPacket[] Packets
        {
            get
            {
                return mPacketList.ToArray();
            }
        }

        #endregion

        #region Methods

        private void LoadTableHeaders()
        {
            PropertyInfo[] infos = typeof(CANPacket).GetProperties();

            foreach (PropertyInfo property in infos)
            {
                if (property.CanRead)
                {
                    tblPacketList.Columns.Add(property.Name, property.Name);
                }
            }

            tblPacketList.Columns.Add("Packet", "Packet");
            
        }

        //----------------------------------------------------------------------------------------

        private void AddPacketToList(CANPacket packet)
        {
            lock (this.mPacketList)
            {
                this.mPacketList.Add(packet);
            }
            lock (this.mChangeList)
            {
                this.mChangeList.Add(packet);
                mPacketListChanged = true;
            }
        }

        //----------------------------------------------------------------------------------------

        private void AddPacketToTable(CANPacket packet)
        {
            if (packet != null)
            {
                PropertyInfo[] propInfos = typeof(CANPacket).GetProperties();

                //Build data for table
                List<object> valueList = new List<object>();
                foreach (PropertyInfo info in propInfos)
                {
                    if (info.Name != "Format")
                    {
                        valueList.Add(info.GetValue(packet, null));
                    }
                }

                //Add packet as last coloumn
                valueList.Add(packet);

                //Add data to table
                tblPacketList.Rows.Add(valueList.ToArray());
            }
        }

        //----------------------------------------------------------------------------------------

        private void ApplyPacketFilter(PacketFilter filter)
        {
            this.Cursor = Cursors.WaitCursor;

            if (filter != null)
            {                
                mCurrentFilter = filter;

                //Set invisible while editing and delete all old packets
                tblPacketList.Rows.Clear();

                //Find packets mathicng the filter
                foreach (CANPacket packet in mPacketList)
                {
                    if (filter.MatchesFilter(packet))
                    {
                        AddPacketToTable(packet);
                    }
                }

            }//if

            this.Cursor = Cursors.Default;
        }

        //----------------------------------------------------------------------------------------

        private void StartCapturing()
        {
            if (mCommunicator != null)
            {
                //Start actualiser
                timPacketListActualiser.Start();

                //Add listener for message received event
                mCommunicator.MessageReceived += new HardwareCommunicator.HardwareMessageDelegate(mCommunicator_MessageReceived);
                
                if (mCommunicator.Start())
                {
                    btnStart.Checked = true;
                    btnStop.Checked = false;
                }
                else
                {
                    timPacketListActualiser.Stop();

                    btnStop.Checked = true;
                    btnStart.Checked = false;

                    MessageBox.Show(this.mCommunicator.Error);
                }
            }
            else
            {
                MessageBox.Show("Communicator is not configured!");
            }
        }

        //----------------------------------------------------------------------------------------

        private void StopCapturing()
        {
            if (mCommunicator != null)
            {
                //Stop actualiser
                timPacketListActualiser.Stop();

                //Stop communicator
                mCommunicator.Stop();

                btnStart.Checked = false;
                btnStop.Checked = true;
            }
        }

        //----------------------------------------------------------------------------------------

        private void Cleanup()
        {
            //Cleanup all things that could be open

            if (mBuilder != null && !mBuilder.IsDisposed)
            {
                mBuilder.Close();
            }
            if (mDiagram != null && !mDiagram.IsDisposed)
            {
                mDiagram.Close();
            }
            if (mCommunicator != null)
            {
                mCommunicator.Stop();
            }
        }

        //----------------------------------------------------------------------------------------

        #endregion

        #region Events

        void mCommunicator_MessageReceived(HardwareMessage message)
        {
            switch (message.MessageType)
            {
                case MessageType.MSG_DATA:
                    this.AddPacketToList(message.Data);
                    this.mWatchFilter.DefaultRule = false;
                    if (this.mWatchFilter.MatchesFilter(message.Data))
                    {
                        //TODO:Packet is watched -> make update in watch window

                        this.mWatch.UpdateValue(message.Data);

                    }
                    break;

                case MessageType.MSG_INFO:
                    //TODO: Handle information messages
                    break;

                case MessageType.MSG_ERROR:
                    this.AddError(message);
                    break;
                case MessageType.MSG_UNKNOWN:
                    this.AddError(String.Format("Unknow message received from hardware\n({0})", message.RawMessageType.ToString("X")));
                    break;
            }//switch
        }

        //----------------------------------------------------------------------------------------

        /// <summary>
        /// Adds an error to the error display box in the mainwindow
        /// </summary>
        /// <param name="message">The hardware message from type error</param>
        private void AddError(HardwareMessage message)
        {
            lock (this.mErrors)
            {
                //Add error to listbox
                this.mErrors.Add("Time: " + message.TimeStamp.ToShortTimeString() + "\n" + message.MessageText);
            }
        }

        /// <summary>
        /// Adds an error to the error display box in the mainwindow
        /// </summary>
        /// <param name="error">The error string that should be displayed</param>
        private void AddError(string error)
        {
            lock (this.mErrors)
            {
                //Add error
                this.mErrors.Add("Time: " + DateTime.Now.ToShortTimeString() + "\n" + error);
            }
        }

        //----------------------------------------------------------------------------------------

        private void btnFilterBuilder_Click(object sender, EventArgs e)
        {
            if (mBuilder == null || mBuilder.IsDisposed)
            {
                mBuilder = new frmFilterBuilder(this);
                mBuilder.Show();
            }
        }

        //----------------------------------------------------------------------------------------

        private void btnApplyFilter_Click(object sender, EventArgs e)
        {
            if (this.FilterString != null)
            {
                this.ApplyPacketFilter(new PacketFilter(this.FilterString));
            }
        }

        //----------------------------------------------------------------------------------------

        private void ltbPackets_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (tblPacketList.SelectedRows != null)
            {
                CANPacket packet = new CANPacket();

                ucDetailView.ActivePacket = packet;
            }
        }

        //----------------------------------------------------------------------------------------

        private void btnDiagram_Click(object sender, EventArgs e)
        {
            if (mDiagram == null || mDiagram.IsDisposed)
            {
                mDiagram = new frmDiagram(this);
                mDiagram.Show();
            }
        }

        //----------------------------------------------------------------------------------------

        private void btnStart_Click(object sender, EventArgs e)
        {
            this.StartCapturing();
        }

        //----------------------------------------------------------------------------------------

        private void btnConfig_Click(object sender, EventArgs e)
        {
            frmConfig conf = new frmConfig();
            if (conf.ShowDialog() == DialogResult.OK)
            {
                if (mCommunicator != null)
                {
                    mCommunicator.Dispose();
                }
                mCommunicator = new HardwareCommunicator(conf.Port);
            }
        }

        //----------------------------------------------------------------------------------------

        private void btnStop_Click(object sender, EventArgs e)
        {
            this.StopCapturing();
        }

        //----------------------------------------------------------------------------------------

        private void timPacketListActualiser_Tick(object sender, EventArgs e)
        {
            if (this.mErrors.Count != 0)
            {
                //Set error box visible if it is hidden
                if (!this.ltbErrors.Visible)
                {
                    this.ltbErrors.Visible = true;                    
                }

                //Set size for holding the new errors (max 5 items at once visible)
                this.Size = new Size(this.Size.Width, this.ltbErrors.ItemHeight * (this.mErrors.Count%5));

                lock (this.mErrors)
                {
                    //Add errors to listbox
                    foreach (string error in this.mErrors)
                    {
                        this.ltbErrors.Items.Add("Time: " + DateTime.Now.ToShortTimeString() + "\n" + error);
                    }
                }
            }

            if (mPacketListChanged && this.mCurrentFilter != null)
            {
                lock (mChangeList)
                {
                    //Add new items to control
                    foreach (CANPacket packet in mChangeList)
                    {
                        if (this.mCurrentFilter.MatchesFilter(packet))
                        {
                            AddPacketToTable(packet);
                        }
                    }

                    if (this.mDiagram != null)
                    {
                        //Update diagram after change
                        this.mDiagram.UpdatePackets(mChangeList.ToArray());
                    }

                    //After new packets have been added -> delete them in the change list
                    mChangeList.Clear();

                }//Lock the list to prevent change while iterating

                mPacketListChanged = false;
            }
        }

        //----------------------------------------------------------------------------------------

        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.Cleanup();
        }

        //----------------------------------------------------------------------------------------
        
        private void btnValueWatch_Click(object sender, EventArgs e)
        {
            if (mWatch == null || mWatch.IsDisposed)
            {
                mWatch = new frmValueWatch(this);
                mWatch.Show();
            }
        }

        //----------------------------------------------------------------------------------------

        private void tblPacketList_SelectionChanged(object sender, EventArgs e)
        {
            if (tblPacketList.SelectedRows.Count > 0)
            {
                ucDetailView.ActivePacket = tblPacketList.SelectedRows[0].Cells["Packet"].Value as CANPacket;
            }
        }

        //----------------------------------------------------------------------------------------

        private void btnAbout_Click(object sender, EventArgs e)
        {
            frmAbout about = new frmAbout();
            about.ShowDialog();
        }

        //----------------------------------------------------------------------------------------

        private void txtFilterString_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                if (this.FilterString != null)
                {
                    this.ApplyPacketFilter(new PacketFilter(this.FilterString));
                }
                e.Handled = true;
            }
        }

        //----------------------------------------------------------------------------------------

        private void txtFilterString_Validating(object sender, CancelEventArgs e)
        {
            MessageBox.Show("VALIDATION");
        }

        //----------------------------------------------------------------------------------------

        private void btnClear_Click(object sender, EventArgs e)
        {
            this.mPacketList.Clear();
            this.tblPacketList.Rows.Clear();
        }

        //----------------------------------------------------------------------------------------

        #endregion


    }
}
